package com.geek.boot;

import com.geek.boot.config.MyConfig1;
import com.geek.boot.config.MyConfig3;
import com.geek.boot.entity.Car;
import com.geek.boot.entity.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Arrays;

/**
 * @author chenmin
 * @desc TODO
 * @date 2025/7/12
 *
 * 复合注解  @SpringBootApplication
 * 1.@SpringBootConfiguration
 *      本质上，@Configuration注解，声明当前是一个配置类，并将其注册至IOC容器中
 *
 * 2.@ComponentScan
 *      指定scanBasePackages扫描包，等同于 @ComponentScan + basePackages
 *      使用场景：自动包规则未生效，自己进行指定扫描包  @SpringBootApplication(scanBasePackages = {"com.geek.boot"})
 *
 * 3.@EnableAutoConfiguration  复合注解，开启自动配置的
 *     3.1 @AutoConfigurationPackage
 *     自动包规则，即找到HelloMainApplication主启动类所在的包，将其包下所有注解标识的类注册至IOC容器中
 *     3.2 @Import(AutoConfigurationImportSelector.class)
 *         加载配置文件，初始化自动配置，不等同于生效，将来按需引入让其生效
 *              -> META-INF -> spring.factories
 *              -> META-INF -> spring -> org.springframework.boot.autoconfigure.AutoConfiguration.imports
 *         将来在pom.xml文件中，引入依赖，进行”按需加载“
 *              -> 引入spring-boot-starter-xxx场景启动器依赖，激活XxxAutoConfiguration中的配置
 *                  -> 定制思路一： 生效条件  @Bean + @ConditionalOnMissingXxx
 *                  JavaConfig配置类Configuration + @Bean 替换底层组件
 *                  -> 定制思路二： 配置绑定  @EnableConfigurationProperties(XxxProperties.class) +  @ConfigurationProperties(prefix = "server")
 *                 在application.properties配置文件中，通过prefix.属性.属性 = 值，从而干涉底层组件的创建
 */

/**
 * 自动配置流程
 * 主启动类 注解@SpringBootApplication -> 注解@EnableAutoConfiguration -> 注解@Import(AutoConfigurationImportSelector.class) -> selectImports()
 * --->  xxx-xxx--autoconfigure.jar -> META-INF -> spring.factories文件 和 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件
 * --->  引入XxxAutoConfiguration自动配置类  ---> 在pom.xml中引入相关依赖，按需加载生效配置
 *
 * 如何定制化开发
 * 定制化思路一：XxxAutoConfiguration自动配置类 ->  配置绑定@EnableConfigurationProperties(XxxProperties.class)  ->  @ConfigurationProperties(prefix = "xxx")
 * 在application.properties 、 application.yml 配置文件中，通过 prefix.属性 = 值
 *
 * 定制化思路二：XxxAutoConfiguration自动配置类 -> @Bean + @ConditionalOnMissingXxx  生效条件
 * 在JavaConfig配置类中，@Configuration + @Bean 替换底层组件
 *
 * 以DispatcherServletAutoConfiguration为例
 *
 * 作业：WebMVCAutoConfiguration 分析
 */
@SpringBootApplication
public class HelloMainApplication {

    public static void main(String[] args) {
        //just run
        ConfigurableApplicationContext ac = SpringApplication.run(HelloMainApplication.class, args);
        //helloMainApplication 、 helloController 、 XxxAutoConfiguration 、XxxProperties
//        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
//        System.out.println("-------------------------------------------");
//        System.out.println(Arrays.toString(beanDefinitionNames));


        //-------------------- MyConfig1 ----------------------
        /*MyConfig1 myConfig1 = ac.getBean(MyConfig1.class);
        //Full模式  myConfig1 = com.geek.boot.config.MyConfig1$$EnhancerBySpringCGLIB$$6d0d89f0@585c13de  CGLIB动态代理
        //Lite模式  myConfig1 = com.geek.boot.config.MyConfig1@a619c2  目标对象
        System.out.println("myConfig1 = "+myConfig1);
        //p1 = Person(name=李四, age=23, car=com.geek.boot.entity.Car@6ad11a56)
        //p2 = Person(name=张三, age=28, car=com.geek.boot.entity.Car@6ad11a56)

        //p1 = Person(name=李四, age=23, car=com.geek.boot.entity.Car@648ee871)
        //p2 = Person(name=张三, age=28, car=com.geek.boot.entity.Car@375b5b7f)
        Person p1 = ac.getBean("p1", Person.class);
        Person p2 = ac.getBean("p2", Person.class);
        System.out.println("p1 = "+p1);
        System.out.println("p2 = "+p2);*/


        //-------------------- MyConfig2 ----------------------
//        String[] beanNames = ac.getBeanNamesForType(Person.class);
//        System.out.println(Arrays.toString(beanNames));
//
//        Person p = ac.getBean("p", Person.class);
//        System.out.println(p);
        //-------------------- MyConfig3 ----------------------
//        MyConfig3 myConfig3 = ac.getBean(MyConfig3.class);
//        System.out.println("myConfig3 = "+myConfig3);
//
        Car c1 = ac.getBean("c1", Car.class);
        System.out.println("c1 = "+c1);

        //-------------------- MyConfig4 ----------------------
        String[] beanNamesForCar = ac.getBeanNamesForType(Car.class);
        System.out.println("cars = "+Arrays.toString(beanNamesForCar));

        Car c2 = ac.getBean("c2", Car.class);
        System.out.println("c2 = "+c2);
    }

}
